home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / terminal / sysline-.1 / sysline- / sysline-1.1 / BSD / sysline.c < prev   
Encoding:
C/C++ Source or Header  |  1994-05-11  |  29.9 KB  |  1,333 lines

  1. /*
  2.  * Copyright (c) 1980 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted provided
  6.  * that: (1) source distributions retain this entire copyright notice and
  7.  * comment, and (2) distributions including binaries display the following
  8.  * acknowledgement:  ``This product includes software developed by the
  9.  * University of California, Berkeley and its contributors'' in the
  10.  * documentation or other materials provided with the distribution and in
  11.  * all advertising materials mentioning features or use of this software.
  12.  * Neither the name of the University nor the names of its contributors may
  13.  * be used to endorse or promote products derived from this software without
  14.  * specific prior written permission.
  15.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  16.  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  17.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  18.  */
  19.  
  20. #ifndef lint
  21. char copyright[] =
  22. "@(#) Copyright (c) 1980 Regents of the University of California.\n\
  23.  All rights reserved.\n";
  24. #endif /* not lint */
  25.  
  26. #ifndef lint
  27. static char sccsid[] = "@(#)sysline.c    5.16 (Berkeley) 6/24/90";
  28. #endif /* not lint */
  29.  
  30. /*
  31.  * sysline - system status display on 25th line of terminal
  32.  * j.k.foderaro
  33.  *
  34.  * Prints a variety of information on the special status line of terminals
  35.  * that have a status display capability.  Cursor motions, status commands,
  36.  * etc. are gleamed from /etc/termcap.
  37.  * By default, all information is printed, and flags are given on the command
  38.  * line to disable the printing of information.  The information and
  39.  * disabling flags are:
  40.  *
  41.  *  flag    what
  42.  *  -----    ----
  43.  *        time of day
  44.  *        load average and change in load average in the last 5 mins
  45.  *        number of user logged on
  46.  *   -p        # of processes the users owns which are runnable and the
  47.  *          number which are suspended.  Processes whose parent is 1
  48.  *          are not counted.
  49.  *   -l        users who've logged on and off.
  50.  *   -m        summarize new mail which has arrived
  51.  *
  52.  *  <other flags>
  53.  *   -r        use non reverse video
  54.  *   -c        turn off 25th line for 5 seconds before redisplaying.
  55.  *   -b        beep once one the half hour, twice on the hour
  56.  *   +N        refresh display every N seconds.
  57.  *   -i        print pid first thing
  58.  *   -e        do simple print designed for an emacs buffer line
  59.  *   -w        do the right things for a window
  60.  *   -h        print hostname between time and load average
  61.  *   -D        print day/date before time of day
  62.  *   -d        debug mode - print status line data in human readable format
  63.  *   -q        quiet mode - don't output diagnostic messages
  64.  *   -s        print Short (left-justified) line if escapes not allowed
  65.  *   -j        Print left Justified line regardless
  66.  */
  67.  
  68. #define BSD4_2            /* for 4.2 BSD */
  69. #define WHO            /* turn this on always */
  70. #define HOSTNAME        /* 4.1a or greater, with hostname() */
  71. #define RWHO            /* 4.1a or greater, with rwho */
  72. #define VMUNIX            /* turn this on if you are running on vmunix */
  73. #define NEW_BOOTTIME        /* 4.1c or greater */
  74.  
  75. #define NETPREFIX "ucb"
  76. #define DEFDELAY 60        /* update status once per minute */
  77. /*
  78.  * if MAXLOAD is defined, then if the load average exceeded MAXLOAD
  79.  * then the process table will not be scanned and the log in/out data
  80.  * will not be checked.   The purpose of this is to reduced the load
  81.  * on the system when it is loaded.
  82.  */
  83. #define MAXLOAD 6.0
  84.  
  85. #include <stdio.h>
  86. #include <sys/param.h>
  87. #include <sys/types.h>
  88. #include <sys/signal.h>
  89. #include <utmp.h>
  90. #include <ctype.h>
  91. #ifndef BSD4_2
  92. #include <unctrl.h>
  93. #endif
  94. #include <sys/time.h>
  95. #include <sys/stat.h>
  96. #ifdef VMUNIX
  97. #include <nlist.h>
  98. #include <sys/vtimes.h>
  99. #include <sys/proc.h>
  100. #endif
  101. #ifdef pdp11
  102. #include <a.out.h>
  103. #include <sys/proc.h>
  104. #endif
  105. #include <curses.h>
  106. #undef nl
  107. #ifdef TERMINFO
  108. #include <term.h>
  109. #endif TERMINFO
  110.  
  111. #ifdef RWHO
  112. #include <protocols/rwhod.h>
  113.  
  114. #define    DOWN_THRESHOLD    (11 * 60)
  115.  
  116. struct remotehost {
  117.     char *rh_host;
  118.     int rh_file;
  119. } remotehost[10];
  120. int nremotes = 0;
  121. #endif RWHO
  122.  
  123. #include "pathnames.h"
  124.  
  125. struct nlist nl[] = {
  126. #ifdef NEW_BOOTTIME
  127.     { "_boottime" },    /* After 4.1a the label changed to "boottime" */
  128. #else
  129.     { "_bootime" },        /* Under 4.1a and earlier it is "bootime" */
  130. #endif
  131. #define    NL_BOOT 0
  132.     { "_proc" },
  133. #define NL_PROC 1
  134. #ifdef VMUNIX
  135.     { "_nproc" },
  136. #define NL_NPROC 2
  137. #endif
  138.     0
  139. };
  140.  
  141.     /* stuff for the kernel */
  142. int kmem;            /* file descriptor for _PATH_KMEM */
  143. struct proc *proc, *procNPROC;
  144. int nproc;
  145. int procadr;
  146. double avenrun[3];        /* used for storing load averages */
  147.  
  148. /*
  149.  * In order to determine how many people are logged on and who has
  150.  * logged in or out, we read in the /etc/utmp file. We also keep track of
  151.  * the previous utmp file.
  152.  */
  153. int ut = -1;            /* the file descriptor */
  154. struct utmp *new, *old;    
  155. char *status;            /* per tty status bits, see below */
  156. int nentries;            /* number of utmp entries */
  157.     /* string lengths for printing */
  158. #define LINESIZE (sizeof old->ut_line)
  159. #define NAMESIZE (sizeof old->ut_name)
  160. /*
  161.  * Status codes to say what has happened to a particular entry in utmp.
  162.  * NOCH means no change, ON means new person logged on,
  163.  * OFF means person logged off.
  164.  */
  165. #define NOCH    0
  166. #define ON    0x1
  167. #define OFF    0x2
  168.  
  169. #ifdef WHO
  170. char whofilename[100];
  171. char whofilename2[100];
  172. #endif
  173.  
  174. #ifdef HOSTNAME
  175. char hostname[MAXHOSTNAMELEN+1];    /* one more for null termination */
  176. #endif
  177.  
  178. char lockfilename[100];        /* if exists, will prevent us from running */
  179.  
  180.     /* flags which determine which info is printed */
  181. int mailcheck = 1;    /* m - do biff like checking of mail */
  182. int proccheck = 1;    /* p - give information on processes */
  183. int logcheck = 1;     /* l - tell who logs in and out */
  184. int hostprint = 0;    /* h - print out hostname */
  185. int dateprint = 0;    /* h - print out day/date */
  186. int quiet = 0;        /* q - hush diagnostic messages */
  187.  
  188.     /* flags which determine how things are printed */
  189. int clr_bet_ref = 0;    /* c - clear line between refeshes */
  190. int reverse = 1;    /* r - use reverse video */
  191. int shortline = 0;    /* s - short (left-justified) if escapes not allowed */
  192. int leftline = 0;    /* j - left-justified even if escapes allowed */
  193.  
  194.     /* flags which have terminal do random things    */
  195. int beep = 0;        /* b - beep every half hour and twice every hour */
  196. int printid = 0;    /* i - print pid of this process at startup */
  197. int synch = 1;        /* synchronize with clock */
  198.  
  199.     /* select output device (status display or straight output) */
  200. int emacs = 0;        /* e - assume status display */
  201. int window = 0;        /* w - window mode */
  202. int dbug = 0;        /* d - debug */
  203.  
  204. /*
  205.  * used to turn off reverse video every REVOFF times
  206.  * in an attempt to not wear out the phospher.
  207.  */
  208. #define REVOFF 5
  209. int revtime = 1;
  210.  
  211.     /* used by mail checker */
  212. off_t mailsize = 0;
  213. off_t linebeg = 0;        /* place where we last left off reading */
  214.  
  215.     /* things used by the string routines */
  216. int chars;            /* number of printable characters */
  217. char *sp;
  218. char strarr[512];        /* big enough now? */
  219.     /* flags to stringdump() */
  220. char sawmail;            /* remember mail was seen to print bells */
  221. char mustclear;            /* status line messed up */
  222.  
  223.     /* strings which control status line display */
  224. #ifdef TERMINFO
  225. char    *rev_out, *rev_end, *arrows;
  226. char    *tparm();
  227. #else
  228. char    to_status_line[64];
  229. char    from_status_line[64];
  230. char    dis_status_line[64];
  231. char    clr_eol[64];
  232. char    rev_out[20], rev_end[20];
  233. char    *arrows, *bell = "\007";
  234. int    eslok;    /* escapes on status line okay (reverse, cursor addressing) */
  235. int    hasws = 0;    /* is "ws" explicitly defined? */
  236. int    columns;
  237. #define tparm(cap, parm) tgoto((cap), 0, (parm))
  238. char    *tgoto();
  239. #endif
  240.  
  241.     /* to deal with window size changes */
  242. #ifdef SIGWINCH
  243. int sigwinch();
  244. char winchanged;    /* window size has changed since last update */
  245. #endif
  246.  
  247.     /* random globals */
  248. char *username;
  249. char *ourtty;            /* keep track of what tty we're on */
  250. struct stat stbuf, mstbuf;    /* mstbuf for mail check only */
  251. unsigned delay = DEFDELAY;
  252. uid_t uid;
  253. double loadavg = 0.0;        /* current load average */
  254. int users = 0;
  255.  
  256. char *getenv();
  257. char *ttyname();
  258. char *strcpy1();
  259. char *sysrup();
  260. char *calloc();
  261. char *malloc();
  262. int outc();
  263. int erroutc();
  264.  
  265. main(argc,argv)
  266.     register char **argv;
  267. {
  268.     int clearbotl();
  269.     register char *cp;
  270.     char *home;
  271.     extern char *index();
  272.  
  273. #ifdef HOSTNAME
  274.     gethostname(hostname, sizeof hostname - 1);
  275.     if ((cp = index(hostname, '.')) != NULL)
  276.         *cp = '\0';
  277. #endif
  278.  
  279.     for (argv++; *argv != 0; argv++)
  280.         switch (**argv) {
  281.         case '-':
  282.             for (cp = *argv + 1; *cp; cp++) {
  283.                 switch(*cp) {
  284.                 case 'r' :    /* turn off reverse video */
  285.                     reverse = 0;
  286.                     break;
  287.                 case 'c':
  288.                     clr_bet_ref = 1;
  289.                     break;
  290.                 case 'h':
  291.                     hostprint = 1;
  292.                     break;
  293.                 case 'D':
  294.                     dateprint = 1;
  295.                     break;
  296. #ifdef RWHO
  297.                 case 'H':
  298.                     if (argv[1] == 0)
  299.                         break;
  300.                     argv++;
  301.                     if (strcmp(hostname, *argv) &&
  302.                         strcmp(&hostname[sizeof NETPREFIX - 1], *argv))
  303.                         remotehost[nremotes++].rh_host = *argv;
  304.                     break;
  305. #endif RWHO
  306.                 case 'm':
  307.                     mailcheck = 0;
  308.                     break;
  309.                 case 'p':
  310.                     proccheck = 0;
  311.                     break;
  312.                 case 'l':
  313.                     logcheck = 0;
  314.                     break;
  315.                 case 'b':
  316.                     beep = 1;
  317.                     break;
  318.                 case 'i':
  319.                     printid = 1;
  320.                     break;
  321.                 case 'w':
  322.                     window = 1;
  323.                     break;
  324.                 case 'e':
  325.                     emacs = 1;
  326.                     break;
  327.                 case 'd':
  328.                     dbug = 1;
  329.                     break;
  330.                 case 'q':
  331.                     quiet = 1;
  332.                     break;
  333.                 case 's':
  334.                     shortline = 1;
  335.                     break;
  336.                 case 'j':
  337.                     leftline = 1;
  338.                     break;
  339.                 default:
  340.                     fprintf(stderr,
  341.                         "sysline: bad flag: %c\n", *cp);
  342.                 }
  343.             }
  344.             break;
  345.         case '+':
  346.             delay = atoi(*argv + 1);
  347.             if (delay < 10)
  348.                 delay = 10;
  349.             else if (delay > 500)
  350.                 delay = 500;
  351.             synch = 0;    /* no more sync */
  352.             break;
  353.         default:
  354.             fprintf(stderr, "sysline: illegal argument %s\n",
  355.                 argv[0]);
  356.         }
  357.     if (emacs) {
  358.         reverse = 0;
  359.         columns = 79;
  360.     } else    /* if not to emacs window, initialize terminal dependent info */
  361.         initterm();
  362. #ifdef SIGWINCH
  363.     /*
  364.      * When the window size changes and we are the foreground
  365.      * process (true if -w), we get this signal.
  366.      */
  367.     signal(SIGWINCH, sigwinch);
  368. #endif
  369.     getwinsize();        /* get window size from ioctl */
  370.  
  371.     /* immediately fork and let the parent die if not emacs mode */
  372.     if (!emacs && !window && !dbug) {
  373.         if (fork())
  374.             exit(0);
  375.         /* pgrp should take care of things, but ignore them anyway */
  376.         signal(SIGINT, SIG_IGN);
  377.         signal(SIGQUIT, SIG_IGN);
  378. #ifdef VMUNIX
  379.         signal(SIGTTOU, SIG_IGN);
  380. #endif
  381.     }
  382.     /*
  383.      * When we logoff, init will do a "vhangup()" on this
  384.      * tty which turns off I/O access and sends a SIGHUP
  385.      * signal.  We catch this and thereby clear the status
  386.      * display.  Note that a bug in 4.1bsd caused the SIGHUP
  387.      * signal to be sent to the wrong process, so you had to
  388.      * `kill -HUP' yourself in your .logout file.
  389.      * Do the same thing for SIGTERM, which is the default kill
  390.      * signal.
  391.      */
  392.     signal(SIGHUP, clearbotl);
  393.     signal(SIGTERM, clearbotl);
  394.     /*
  395.      * This is so kill -ALRM to force update won't screw us up..
  396.      */
  397.     signal(SIGALRM, SIG_IGN);
  398.  
  399.     uid = getuid();
  400.     ourtty = ttyname(2);    /* remember what tty we are on */
  401.     if (printid) {
  402.         printf("%d\n", getpid());
  403.         fflush(stdout);
  404.     }
  405.     dup2(2, 1);
  406.  
  407.     if ((home = getenv("HOME")) == 0)
  408.         home = "";
  409.     strcpy1(strcpy1(whofilename, home), "/.who");
  410.     strcpy1(strcpy1(whofilename2, home), "/.sysline");
  411.     strcpy1(strcpy1(lockfilename, home), "/.syslinelock");
  412.  
  413.     if ((kmem = open(_PATH_KMEM,0)) < 0) {
  414.         fprintf(stderr, "Can't open %s\n", _PATH_KMEM);
  415.         exit(1);
  416.     }
  417.     readnamelist();
  418.     if (proccheck)
  419.         initprocread();
  420.     if (mailcheck)
  421.         if ((username = getenv("USER")) == 0)
  422.             mailcheck = 0;
  423.         else {
  424.             chdir(_PATH_MAILDIR);
  425.             if (stat(username, &mstbuf) >= 0)
  426.                 mailsize = mstbuf.st_size;
  427.             else
  428.                 mailsize = 0;
  429.         }
  430.  
  431.     while (emacs || window || isloggedin())
  432.         if (access(lockfilename, 0) >= 0)
  433.             sleep(60);
  434.         else {
  435.             prtinfo();
  436.             sleep(delay);
  437.             if (clr_bet_ref) {
  438.                 tputs(dis_status_line, 1, outc);
  439.                 fflush(stdout);
  440.                 sleep(5);
  441.             }
  442.             revtime = (1 + revtime) % REVOFF;
  443.         }
  444.     clearbotl();
  445.     /*NOTREACHED*/
  446. }
  447.  
  448. isloggedin()
  449. {
  450.     /*
  451.      * you can tell if a person has logged out if the owner of
  452.      * the tty has changed
  453.      */
  454.     struct stat statbuf;
  455.  
  456.     return fstat(2, &statbuf) == 0 && statbuf.st_uid == uid;
  457. }
  458.  
  459. readnamelist()
  460. {
  461.     time_t bootime, clock, nintv, time();
  462.  
  463.     nlist(_PATH_UNIX, nl);
  464.     if (nl[0].n_value == 0) {
  465.         if (!quiet)
  466.             fprintf(stderr, "No namelist\n");
  467.         return;
  468.     }
  469.     lseek(kmem, (long)nl[NL_BOOT].n_value, 0);
  470.     read(kmem, &bootime, sizeof(bootime));
  471.     (void) time(&clock);
  472.     nintv = clock - bootime;
  473.     if (nintv <= 0L || nintv > 60L*60L*24L*365L) {
  474.         if (!quiet)
  475.             fprintf(stderr,
  476.             "Time makes no sense... namelist must be wrong\n");
  477.         nl[NL_PROC].n_value = 0;
  478.     }
  479. }
  480.  
  481. readutmp(nflag)
  482.     char nflag;
  483. {
  484.     static time_t lastmod;        /* initially zero */
  485.     static off_t utmpsize;        /* ditto */
  486.     struct stat st;
  487.  
  488.     if (ut < 0 && (ut = open(_PATH_UTMP, 0)) < 0) {
  489.         fprintf(stderr, "sysline: Can't open %s.\n", _PATH_UTMP);
  490.         exit(1);
  491.     }
  492.     if (fstat(ut, &st) < 0 || st.st_mtime == lastmod)
  493.         return 0;
  494.     lastmod = st.st_mtime;
  495.     if (utmpsize != st.st_size) {
  496.         utmpsize = st.st_size;
  497.         nentries = utmpsize / sizeof (struct utmp);
  498.         if (old == 0) {
  499.             old = (struct utmp *)calloc(utmpsize, 1);
  500.             new = (struct utmp *)calloc(utmpsize, 1);
  501.         } else {
  502.             old = (struct utmp *)realloc((char *)old, utmpsize);
  503.             new = (struct utmp *)realloc((char *)new, utmpsize);
  504.             free(status);
  505.         }
  506.         status = malloc(nentries * sizeof *status);
  507.         if (old == 0 || new == 0 || status == 0) {
  508.             fprintf(stderr, "sysline: Out of memory.\n");
  509.             exit(1);
  510.         }
  511.     }
  512.     lseek(ut, 0L, 0);
  513.     (void) read(ut, (char *) (nflag ? new : old), utmpsize);
  514.     return 1;
  515. }
  516.  
  517. /*
  518.  * read in the process table locations and sizes, and allocate space
  519.  * for storing the process table.  This is done only once.
  520.  */
  521. initprocread()
  522. {
  523.  
  524.     if (nl[NL_PROC].n_value == 0)
  525.         return;
  526. #ifdef VMUNIX
  527.     lseek(kmem, (long)nl[NL_PROC].n_value, 0);
  528.     read(kmem, &procadr, sizeof procadr);
  529.     lseek(kmem, (long)nl[NL_NPROC].n_value, 0);
  530.     read(kmem, &nproc, sizeof nproc);
  531. #endif
  532. #ifdef pdp11
  533.     procadr = nl[NL_PROC].n_value;
  534.     nproc = NPROC;            /* from param.h */
  535. #endif
  536.     if ((proc = (struct proc *) calloc(nproc, sizeof (struct proc))) == 0) {
  537.         fprintf(stderr, "Out of memory.\n");
  538.         exit(1);
  539.     }
  540.     procNPROC = proc + nproc;
  541. }
  542.  
  543. /*
  544.  * read in the process table.  This assumes that initprocread has alread been
  545.  * called to set up storage.
  546.  */
  547. readproctab()
  548. {
  549.  
  550.     if (nl[NL_PROC].n_value == 0)
  551.         return (0);
  552.     lseek(kmem, (long)procadr, 0);
  553.     read(kmem, (char *)proc, nproc * sizeof (struct proc));
  554.     return (1);
  555. }
  556.  
  557. prtinfo()
  558. {
  559.     int on, off;
  560.     register i;
  561.     char fullprocess;
  562.  
  563.     stringinit();
  564. #ifdef SIGWINCH
  565.     if (winchanged) {
  566.         winchanged = 0;
  567.         getwinsize();
  568.         mustclear = 1;
  569.     }
  570. #endif
  571. #ifdef WHO
  572.     /* check for file named .who in the home directory */
  573.     whocheck();
  574. #endif
  575.     timeprint();
  576.     /*
  577.      * if mail is seen, don't print rest of info, just the mail
  578.      * reverse new and old so that next time we run, we won't lose log
  579.      * in and out information
  580.      */
  581.     if (mailcheck && (sawmail = mailseen()))
  582.         goto bottom;
  583. #ifdef HOSTNAME
  584. #ifdef RWHO
  585.     for (i = 0; i < nremotes; i++) {
  586.         char *tmp;
  587.  
  588.         stringspace();
  589.         tmp = sysrup(remotehost + i);
  590.         stringcat(tmp, strlen(tmp));
  591.     }
  592. #endif
  593.     /*
  594.      * print hostname info if requested
  595.      */
  596.     if (hostprint) {
  597.         stringspace();
  598.         stringcat(hostname, -1);
  599.     }
  600. #endif
  601.     /*
  602.      * print load average and difference between current load average
  603.      * and the load average 5 minutes ago
  604.      */
  605.     if (getloadavg(avenrun, 3) > 0) {
  606.         double diff;
  607.  
  608.         stringspace();
  609.  
  610.         if ((diff = avenrun[0] - avenrun[1]) < 0.0)
  611.             stringprt("%.1f %.1f", avenrun[0],  diff);
  612.         else
  613.             stringprt("%.1f +%.1f", avenrun[0], diff);
  614.         loadavg = avenrun[0];        /* remember load average */
  615.     }
  616.     /*
  617.      * print log on and off information
  618.      */
  619.     stringspace();
  620.     fullprocess = 1;
  621. #ifdef MAXLOAD
  622.     if (loadavg > MAXLOAD)
  623.         fullprocess = 0;    /* too loaded to run */
  624. #endif
  625.     /*
  626.      * Read utmp file (logged in data) only if we are doing a full
  627.      * process, or if this is the first time and we are calculating
  628.      * the number of users.
  629.      */
  630.     on = off = 0;
  631.     if (users == 0) {        /* first time */
  632.         if (readutmp(0))
  633.             for (i = 0; i < nentries; i++)
  634.                 if (old[i].ut_name[0])
  635.                     users++;
  636.     } else if (fullprocess && readutmp(1)) {
  637.         struct utmp *tmp;
  638.  
  639.         users = 0;
  640.         for (i = 0; i < nentries; i++) {
  641.             if (strncmp(old[i].ut_name,
  642.                 new[i].ut_name, NAMESIZE) == 0)
  643.                 status[i] = NOCH;
  644.             else if (old[i].ut_name[0] == '\0') {
  645.                 status[i] = ON;
  646.                 on++;
  647.             } else if (new[i].ut_name[0] == '\0') {
  648.                 status[i] = OFF;
  649.                 off++;
  650.             } else {
  651.                 status[i] = ON | OFF;
  652.                 on++;
  653.                 off++;
  654.             }
  655.             if (new[i].ut_name[0])
  656.                 users++;
  657.         }
  658.         tmp = new;
  659.         new = old;
  660.         old = tmp;
  661.     }
  662.     /*
  663.      * Print:
  664.      *     1.  number of users
  665.      *    2.  a * for unread mail
  666.      *    3.  a - if load is too high
  667.      *    4.  number of processes running and stopped
  668.      */
  669.     stringprt("%du", users);
  670.     if (mailsize > 0 && mstbuf.st_mtime >= mstbuf.st_atime)
  671.         stringcat("*", -1);
  672.     if (!fullprocess && (proccheck || logcheck))
  673.         stringcat("-", -1);
  674.     if (fullprocess && proccheck && readproctab()) {
  675.         register struct proc *p;
  676.         int procrun, procstop;
  677.  
  678.         /*
  679.          * We are only interested in processes which have the same
  680.          * uid as us, and whose parent process id is not 1.
  681.          */
  682.         procrun = procstop = 0;
  683.         for (p = proc; p < procNPROC; p++) {
  684.             if (p->p_stat == 0 || p->p_pgrp == 0 ||
  685.                 p->p_uid != uid || p->p_ppid == 1)
  686.                 continue;
  687.             switch (p->p_stat) {
  688.             case SSTOP:
  689.                 procstop++;
  690.                 break;
  691.             case SSLEEP:
  692.                 /*
  693.                  * Sleep can mean waiting for a signal or just
  694.                  * in a disk or page wait queue ready to run.
  695.                  * We can tell if it is the later by the pri
  696.                  * being negative.
  697.                  */
  698.                 if (p->p_pri < PZERO)
  699.                     procrun++;
  700.                 break;
  701.             case SWAIT:
  702.             case SRUN:
  703.             case SIDL:
  704.                 procrun++;
  705.             }
  706.         }
  707.         if (procrun > 0 || procstop > 0) {
  708.             stringspace();
  709.             if (procrun > 0 && procstop > 0)
  710.                 stringprt("%dr %ds", procrun, procstop);
  711.             else if (procrun > 0)
  712.                 stringprt("%dr", procrun);
  713.             else
  714.                 stringprt("%ds", procstop);
  715.         }
  716.     }
  717.     /*
  718.      * If anyone has logged on or off, and we are interested in it,
  719.      * print it out.
  720.      */
  721.     if (logcheck) {
  722.         /* old and new have already been swapped */
  723.         if (on) {
  724.             stringspace();
  725.             stringcat("on:", -1);
  726.             for (i = 0; i < nentries; i++)
  727.                 if (status[i] & ON) {
  728.                     stringprt(" %.8s", old[i].ut_name);
  729.                     ttyprint(old[i].ut_line);
  730.                 }
  731.         }
  732.         if (off) {
  733.             stringspace();
  734.             stringcat("off:", -1);
  735.             for (i = 0; i < nentries; i++)
  736.                 if (status[i] & OFF) {
  737.                     stringprt(" %.8s", new[i].ut_name);
  738.                     ttyprint(new[i].ut_line);
  739.                 }
  740.         }
  741.     }
  742. bottom:
  743.         /* dump out what we know */
  744.     stringdump();
  745. }
  746.  
  747. timeprint()
  748. {
  749.     long curtime;
  750.     struct tm *tp, *localtime();
  751.     static int beepable = 1;
  752.  
  753.         /* always print time */
  754.     time(&curtime);
  755.     tp = localtime(&curtime);
  756.     if (dateprint)
  757.         stringprt("%.11s", ctime(&curtime));
  758.     stringprt("%d:%02d", tp->tm_hour > 12 ? tp->tm_hour - 12 :
  759.         (tp->tm_hour == 0 ? 12 : tp->tm_hour), tp->tm_min);
  760.     if (synch)            /* sync with clock */
  761.         delay = 60 - tp->tm_sec;
  762.     /*
  763.      * Beepable is used to insure that we get at most one set of beeps
  764.      * every half hour.
  765.      */
  766.     if (beep)
  767.         if (beepable) {
  768.             if (tp->tm_min == 30) {
  769.                 tputs(bell, 1, outc);
  770.                 fflush(stdout);
  771.                 beepable = 0;
  772.             } else if (tp->tm_min == 0) {
  773.                 tputs(bell, 1, outc);
  774.                 fflush(stdout);
  775.                 sleep(2);
  776.                 tputs(bell, 1, outc);
  777.                 fflush(stdout);
  778.                 beepable = 0;
  779.             }
  780.         } else
  781.             if (tp->tm_min != 0 && tp->tm_min != 30)
  782.                 beepable = 1;
  783. }
  784.  
  785. /*
  786.  * whocheck -- check for file named .who and print it on the who line first
  787.  */
  788. whocheck()
  789. {
  790.     int chss;
  791.     register char *p;
  792.     char buff[81];
  793.     int whofile;
  794.  
  795.     if ((whofile = open(whofilename, 0)) < 0 &&
  796.         (whofile = open(whofilename2, 0)) < 0)
  797.         return;
  798.     chss = read(whofile, buff, sizeof buff - 1);
  799.     close(whofile);
  800.     if (chss <= 0)
  801.         return;
  802.     buff[chss] = '\0';
  803.     /*
  804.      * Remove all line feeds, and replace by spaces if they are within
  805.      * the message, else replace them by nulls.
  806.      */
  807.     for (p = buff; *p;)
  808.         if (*p == '\n')
  809.             if (p[1])
  810.                 *p++ = ' ';
  811.             else
  812.                 *p = '\0';
  813.         else
  814.             p++;
  815.     stringcat(buff, p - buff);
  816.     stringspace();
  817. }
  818.  
  819. /*
  820.  * ttyprint -- given the name of a tty, print in the string buffer its
  821.  * short name surrounded by parenthesis.
  822.  * ttyxx is printed as (xx)
  823.  * console is printed as (cty)
  824.  */
  825. ttyprint(name)
  826.     char *name;
  827. {
  828.     char buff[11];
  829.  
  830.     if (strncmp(name, "tty", 3) == 0)
  831.         stringprt("(%.*s)", LINESIZE - 3, name + 3);
  832.     else if (strcmp(name, "console") == 0)
  833.         stringcat("(cty)", -1);
  834.     else
  835.         stringprt("(%.*s)", LINESIZE, name);
  836. }
  837.  
  838. /*
  839.  * mail checking function
  840.  * returns 0 if no mail seen
  841.  */
  842. mailseen()
  843. {
  844.     FILE *mfd;
  845.     register n;
  846.     register char *cp;
  847.     char lbuf[100], sendbuf[100], *bufend;
  848.     char seenspace;
  849.     int retval = 0;
  850.  
  851.     if (stat(username, &mstbuf) < 0) {
  852.         mailsize = 0;
  853.         return 0;
  854.     }
  855.     if (mstbuf.st_size <= mailsize || (mfd = fopen(username,"r")) == NULL) {
  856.         mailsize = mstbuf.st_size;
  857.         return 0;
  858.     }
  859.     fseek(mfd, mailsize, 0);
  860.     while ((n = readline(mfd, lbuf, sizeof lbuf)) >= 0 &&
  861.            strncmp(lbuf, "From ", 5) != 0)
  862.         ;
  863.     if (n < 0) {
  864.         stringcat("Mail has just arrived", -1);
  865.         goto out;
  866.     }
  867.     retval = 1;
  868.     /*
  869.      * Found a From line, get second word, which is the sender,
  870.      * and print it.
  871.      */
  872.     for (cp = lbuf + 5; *cp && *cp != ' '; cp++)    /* skip to blank */
  873.         ;
  874.     *cp = '\0';                    /* terminate name */
  875.     stringspace();
  876.     stringprt("Mail from %s ", lbuf + 5);
  877.     /*
  878.      * Print subject, and skip over header.
  879.      */
  880.     while ((n = readline(mfd, lbuf, sizeof lbuf)) > 0)
  881.         if (strncmp(lbuf, "Subject:", 8) == 0)
  882.             stringprt("on %s ", lbuf + 9);
  883.     if (!emacs)
  884.         stringcat(arrows, 2);
  885.     else
  886.         stringcat(": ", 2);
  887.     if (n < 0)            /* already at eof */
  888.         goto out;
  889.     /*
  890.      * Print as much of the letter as we can.
  891.      */
  892.     cp = sendbuf;
  893.     if ((n = columns - chars) > sizeof sendbuf - 1)
  894.         n = sizeof sendbuf - 1;
  895.     bufend = cp + n;
  896.     seenspace = 0;
  897.     while ((n = readline(mfd, lbuf, sizeof lbuf)) >= 0) {
  898.         register char *rp;
  899.  
  900.         if (strncmp(lbuf, "From ", 5) == 0)
  901.             break;
  902.         if (cp >= bufend)
  903.             continue;
  904.         if (!seenspace) {
  905.             *cp++ = ' ';        /* space before lines */
  906.             seenspace = 1;
  907.         }
  908.         rp = lbuf;
  909.         while (*rp && cp < bufend)
  910.             if (isspace(*rp)) {
  911.                 if (!seenspace) {
  912.                     *cp++ = ' ';
  913.                     seenspace = 1;
  914.                 }
  915.                 rp++;
  916.             } else {
  917.                 *cp++ = *rp++;
  918.                 seenspace = 0;
  919.             }
  920.     }
  921.     *cp = 0;
  922.     stringcat(sendbuf, -1);
  923.     /*
  924.      * Want to update write time so a star will
  925.      * appear after the number of users until the
  926.      * user reads his mail.
  927.      */
  928. out:
  929.     mailsize = linebeg;
  930.     fclose(mfd);
  931.     touch(username);
  932.     return retval;
  933. }
  934.  
  935. /*
  936.  * readline -- read a line from fp and store it in buf.
  937.  * return the number of characters read.
  938.  */
  939. readline(fp, buf, n)
  940.     register FILE *fp;
  941.     char *buf;
  942.     register n;
  943. {
  944.     register c;
  945.     register char *cp = buf;
  946.  
  947.     linebeg = ftell(fp);        /* remember loc where line begins */
  948.     cp = buf;
  949.     while (--n > 0 && (c = getc(fp)) != EOF && c != '\n')
  950.         *cp++ = c;
  951.     *cp = 0;
  952.     if (c == EOF && cp - buf == 0)
  953.         return -1;
  954.     return cp - buf;
  955. }
  956.  
  957.  
  958. /*
  959.  * string hacking functions
  960.  */
  961.  
  962. stringinit()
  963. {
  964.     sp = strarr;
  965.     chars = 0;
  966. }
  967.  
  968. /*VARARGS1*/
  969. stringprt(format, a, b, c)
  970.     char *format;
  971. {
  972.     char tempbuf[150];
  973.  
  974.     (void)sprintf(tempbuf, format, a, b, c);
  975.     stringcat(tempbuf, -1);
  976. }
  977.  
  978. stringdump()
  979. {
  980.     char bigbuf[sizeof strarr + 200];
  981.     register char *bp = bigbuf;
  982.     register int i;
  983.  
  984.     if (!emacs) {
  985.         if (sawmail)
  986.             bp = strcpy1(bp, bell);
  987.         if (eslok)
  988.             bp = strcpy1(bp, tparm(to_status_line,
  989.                 leftline ? 0 : columns - chars));
  990.         else {
  991.             bp = strcpy1(bp, to_status_line);
  992.             if (!shortline && !leftline)
  993.                 for (i = columns - chars; --i >= 0;)
  994.                     *bp++ = ' ';
  995.         }
  996.         if (reverse && revtime != 0)
  997.             bp = strcpy1(bp, rev_out);
  998.     }
  999.     *sp = 0;
  1000.     bp = strcpy1(bp, strarr);
  1001.     if (!emacs) {
  1002.         if (reverse)
  1003.             bp = strcpy1(bp, rev_end);
  1004.         bp = strcpy1(bp, from_status_line);
  1005.         if (sawmail)
  1006.             bp = strcpy1(strcpy1(bp, bell), bell);
  1007.         *bp = 0;
  1008.         tputs(bigbuf, 1, outc);
  1009.         if (mustclear) {
  1010.             mustclear = 0;
  1011.             tputs(clr_eol, 1, outc);
  1012.         }
  1013.         if (dbug)
  1014.             putchar('\n');
  1015.         fflush(stdout);
  1016.     } else
  1017.         write(2, bigbuf, bp - bigbuf);
  1018. }
  1019.  
  1020. stringspace()
  1021. {
  1022.     if (reverse && revtime != 0) {
  1023. #ifdef TERMINFO
  1024.         stringcat(rev_end,
  1025.             magic_cookie_glitch <= 0 ? 0 : magic_cookie_glitch);
  1026.         stringcat(" ", 1);
  1027.         stringcat(rev_out,
  1028.             magic_cookie_glitch <= 0 ? 0 : magic_cookie_glitch);
  1029. #else
  1030.         stringcat(rev_end, 0);
  1031.         stringcat(" ", 1);
  1032.         stringcat(rev_out, 0);
  1033. #endif TERMINFO
  1034.     } else
  1035.         stringcat(" ", 1);
  1036. }
  1037.  
  1038. /*
  1039.  * stringcat :: concatenate the characters in string str to the list we are
  1040.  *             building to send out.
  1041.  * str - the string to print. may contain funny (terminal control) chars.
  1042.  * n  - the number of printable characters in the string
  1043.  *    or if -1 then str is all printable so we can truncate it,
  1044.  *    otherwise don't print only half a string.
  1045.  */
  1046. stringcat(str, n)
  1047.     register char *str;
  1048.     register n;
  1049. {
  1050.     register char *p = sp;
  1051.  
  1052.     if (n < 0) {                /* truncate */
  1053.         n = columns - chars;
  1054.         while ((*p++ = *str++) && --n >= 0)
  1055.             ;
  1056.         p--;
  1057.         chars += p - sp;
  1058.         sp = p;
  1059.     } else if (chars + n <= columns) {    /* don't truncate */
  1060.         while (*p++ = *str++)
  1061.             ;
  1062.         chars += n;
  1063.         sp = p - 1;
  1064.     }
  1065. }
  1066.  
  1067. /*
  1068.  * touch :: update the modify time of a file.
  1069.  */
  1070. touch(name)
  1071.     char *name;        /* name of file */
  1072. {
  1073.     register fd;
  1074.     char buf;
  1075.  
  1076.     if ((fd = open(name, 2)) >= 0) {
  1077.         read(fd, &buf, 1);        /* get first byte */
  1078.         lseek(fd, 0L, 0);        /* go to beginning */
  1079.         write(fd, &buf, 1);        /* and rewrite first byte */
  1080.         close(fd);
  1081.     }
  1082. }
  1083.  
  1084.  
  1085. /*
  1086.  * clearbotl :: clear bottom line.
  1087.  * called when process quits or is killed.
  1088.  * it clears the bottom line of the terminal.
  1089.  */
  1090. clearbotl()
  1091. {
  1092.     register int fd;
  1093.     int exit();
  1094.  
  1095.     signal(SIGALRM, exit);
  1096.     alarm(30);    /* if can't open in 30 secs, just die */
  1097.     if (!emacs && (fd = open(ourtty, 1)) >= 0) {
  1098.         write(fd, dis_status_line, strlen(dis_status_line));
  1099.         close(fd);
  1100.     }
  1101. #ifdef PROF
  1102.     if (chdir(_PATH_SYSLINE) < 0)
  1103.         (void) chdir(_PATH_TMP);
  1104. #endif
  1105.     exit(0);
  1106. }
  1107.  
  1108. #ifdef TERMINFO
  1109. initterm()
  1110. {
  1111.     static char standbuf[40];
  1112.  
  1113.     setupterm(0, 1, 0);
  1114.     if (!window && !has_status_line) {
  1115.         /* not an appropriate terminal */
  1116.         if (!quiet)
  1117.            fprintf(stderr, "sysline: no status capability for %s\n",
  1118.             getenv("TERM"));
  1119.         exit(1);
  1120.     }
  1121.     if (window || status_line_esc_ok) {
  1122.         if (set_attributes) {
  1123.             /* reverse video mode */
  1124.             strcpy(standbuf,
  1125.                 tparm(set_attributes,0,0,1,0,0,0,0,0,0));
  1126.             rev_out = standbuf;
  1127.             rev_end = exit_attribute_mode;
  1128.         } else if (enter_standout_mode && exit_standout_mode) {
  1129.             rev_out = enter_standout_mode;
  1130.             rev_end = exit_standout_mode;
  1131.         } else
  1132.             rev_out = rev_end = "";
  1133.     } else
  1134.         rev_out = rev_end = "";
  1135.     columns--;    /* avoid cursor wraparound */
  1136. }
  1137.  
  1138. #else    /* TERMCAP */
  1139.  
  1140. initterm()
  1141. {
  1142.     char *term, *cp;
  1143.     static char tbuf[1024];
  1144.     char is2[40];
  1145.     extern char *UP;
  1146.  
  1147.     if ((term = getenv("TERM")) == NULL) {
  1148.         if (!quiet)
  1149.             fprintf(stderr,
  1150.                 "sysline: No TERM variable in enviroment\n");
  1151.         exit(1);
  1152.     }
  1153.     if (tgetent(tbuf, term) <= 0) {
  1154.         if (!quiet)
  1155.             fprintf(stderr,
  1156.                 "sysline: Unknown terminal type: %s\n", term);
  1157.         exit(1);
  1158.     }
  1159.     if (!window && tgetflag("hs") <= 0) {
  1160.         if (!strncmp(term, "h19", 3)) {
  1161.             /* for upward compatability with h19sys */
  1162.             strcpy(to_status_line,
  1163.                 "\033j\033x5\033x1\033Y8%+ \033o");
  1164.             strcpy(from_status_line, "\033k\033y5");
  1165.             strcpy(dis_status_line, "\033y1");
  1166.             strcpy(rev_out, "\033p");
  1167.             strcpy(rev_end, "\033q");
  1168.             arrows = "\033Fhh\033G";
  1169.             columns = 80;
  1170.             UP = "\b";
  1171.             return;
  1172.         }
  1173.         if (!quiet)
  1174.             fprintf(stderr,
  1175.                 "sysline: No status capability for %s\n", term);
  1176.         exit(1);
  1177.     }
  1178.     cp = is2;
  1179.     if (tgetstr("i2", &cp) != NULL) {
  1180.         /* someday tset will do this */
  1181.         tputs(is2, 1, erroutc);
  1182.         fflush(stdout);
  1183.     }
  1184.  
  1185.     /* the "-1" below is to avoid cursor wraparound problems */
  1186.     columns = tgetnum("ws");
  1187.     hasws = columns >= 0;
  1188.     if (!hasws)
  1189.         columns = tgetnum("co");
  1190.     columns -= 1;
  1191.     if (window) {
  1192.         strcpy(to_status_line, "\r");
  1193.         cp = dis_status_line;    /* use the clear line sequence */
  1194.         *cp++ = '\r';
  1195.         tgetstr("ce", &cp);
  1196.         if (leftline)
  1197.             strcpy(from_status_line, dis_status_line + 1);
  1198.         else
  1199.             strcpy(from_status_line, "");
  1200.     } else {
  1201.         cp = to_status_line;
  1202.         tgetstr("ts", &cp);
  1203.         cp = from_status_line;
  1204.         tgetstr("fs", &cp);
  1205.         cp = dis_status_line;
  1206.         tgetstr("ds", &cp);
  1207.         eslok = tgetflag("es");
  1208.     }
  1209.     if (eslok || window) {
  1210.         cp = rev_out;
  1211.         tgetstr("so", &cp);
  1212.         cp = rev_end;
  1213.         tgetstr("se", &cp);
  1214.         cp = clr_eol;
  1215.         tgetstr("ce", &cp);
  1216.     } else
  1217.         reverse = 0;    /* turn off reverse video */
  1218.     UP = "\b";
  1219.     if (!strncmp(term, "h19", 3))
  1220.         arrows = "\033Fhh\033G";    /* "two tiny graphic arrows" */
  1221.     else
  1222.         arrows = "->";
  1223. }
  1224. #endif TERMINFO
  1225.  
  1226. #ifdef RWHO
  1227. char *
  1228. sysrup(hp)
  1229.     register struct remotehost *hp;
  1230. {
  1231.     char filename[100];
  1232.     struct whod wd;
  1233. #define WHOD_HDR_SIZE (sizeof (wd) - sizeof (wd.wd_we))
  1234.     static char buffer[50];
  1235.     time_t now;
  1236.  
  1237.     /*
  1238.      * rh_file is initially 0.
  1239.      * This is ok since standard input is assumed to exist.
  1240.      */
  1241.     if (hp->rh_file == 0) {
  1242.         /*
  1243.          * Try rwho hostname file, and if that fails try ucbhostname.
  1244.          */
  1245.         (void) strcpy1(strcpy1(filename, _PATH_RWHO), hp->rh_host);
  1246.         if ((hp->rh_file = open(filename, 0)) < 0) {
  1247.             (void) strcpy1(strcpy1(strcpy1(filename, _PATH_RWHO),
  1248.                 NETPREFIX), hp->rh_host);
  1249.             hp->rh_file = open(filename, 0);
  1250.         }
  1251.     }
  1252.     if (hp->rh_file < 0) {
  1253.         (void) sprintf(buffer, "%s?", hp->rh_host);
  1254.         return(buffer);
  1255.     }
  1256.     (void) lseek(hp->rh_file, (off_t)0, 0);
  1257.     if (read(hp->rh_file, (char *)&wd, WHOD_HDR_SIZE) != WHOD_HDR_SIZE) {
  1258.         (void) sprintf(buffer, "%s ?", hp->rh_host);
  1259.         return(buffer);
  1260.     }
  1261.     (void) time(&now);
  1262.     if (now - wd.wd_recvtime > DOWN_THRESHOLD) {
  1263.         long interval;
  1264.         long days, hours, minutes;
  1265.  
  1266.         interval = now - wd.wd_recvtime;
  1267.         minutes = (interval + 59) / 60;    /* round to minutes */
  1268.         hours = minutes / 60;        /* extract hours from minutes */
  1269.         minutes %= 60;            /* remove hours from minutes */
  1270.         days = hours / 24;        /* extract days from hours */
  1271.         hours %= 24;            /* remove days from hours */
  1272.         if (days > 7 || days < 0)
  1273.             (void) sprintf(buffer, "%s down", hp->rh_host);
  1274.         else if (days > 0)
  1275.             (void) sprintf(buffer, "%s %d+%d:%02d",
  1276.                 hp->rh_host, days, hours, minutes);
  1277.         else
  1278.             (void) sprintf(buffer, "%s %d:%02d",
  1279.                 hp->rh_host, hours, minutes);
  1280.     } else
  1281.         (void) sprintf(buffer, "%s %.1f",
  1282.             hp->rh_host, wd.wd_loadav[0]/100.0);
  1283.     return buffer;
  1284. }
  1285. #endif RWHO
  1286.  
  1287. getwinsize()
  1288. {
  1289. #ifdef TIOCGWINSZ
  1290.     struct winsize winsize;
  1291.  
  1292.     /* the "-1" below is to avoid cursor wraparound problems */
  1293.     if (!hasws && ioctl(2, TIOCGWINSZ, (char *)&winsize) >= 0 &&
  1294.         winsize.ws_col != 0)
  1295.         columns = winsize.ws_col - 1;
  1296. #endif
  1297. }
  1298.  
  1299. #ifdef SIGWINCH
  1300. sigwinch()
  1301. {
  1302.     winchanged++;
  1303. }
  1304. #endif
  1305.  
  1306. char *
  1307. strcpy1(p, q)
  1308.     register char *p, *q;
  1309. {
  1310.  
  1311.     while (*p++ = *q++)
  1312.         ;
  1313.     return p - 1;
  1314. }
  1315.  
  1316. outc(c)
  1317.     char c;
  1318. {
  1319.     if (dbug)
  1320.         printf("%s", unctrl(c));
  1321.     else
  1322.         putchar(c);
  1323. }
  1324.  
  1325. erroutc(c)
  1326.     char c;
  1327. {
  1328.     if (dbug)
  1329.         fprintf(stderr, "%s", unctrl(c));
  1330.     else
  1331.         putc(c, stderr);
  1332. }
  1333.